// Copyright 2013 Ronald Schlenker, www.technewlogic.de.
// 
// This file is part of Byond.
// 
// Byond is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
// 
// Byond is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with Byond.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using Byond.OnlineDebugging;
using Microsoft.CSharp;
using Mvvm;
using Byond.Logging;

namespace Byond.Frontend.OnlineDebugging.ViewModels
{
	class DebugEditorViewModel : ViewModelBase
	{
		public DebugEditorViewModel(
			DebuggingHostObject hostObject,
			ObjectInspectorViewModel objectInspector,
			Lazy<IUserLogger> logger)
		{
			_hostObject = hostObject;
			_logger = logger;
			ObjectInspector = objectInspector;
			SourceCode = "return Host.Sequencer.GetTime(0);";
		}

		private readonly DebuggingHostObject _hostObject;
		private readonly Lazy<IUserLogger> _logger;

		public ObjectInspectorViewModel ObjectInspector { get; private set; }

		private string _sourceCode;
		public string SourceCode
		{
			get { return _sourceCode; }
			set
			{
				_sourceCode = value;
				OnPropertyChanged("SourceCode");
			}
		}

		public void Execute()
		{
			try
			{
				var o = CompileAndExecute();
				ObjectInspector.UpdateInstance(o);
			}
			catch (Exception e)
			{
				_logger.Value.Log("Execution failed: " + e);
			}
		}

		private object CompileAndExecute()
		{
			var provider = new CSharpCodeProvider(
				new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
			var cp = new CompilerParameters();

			var ver = string.Format("{0}.{1}.{2}", Environment.Version.Major, Environment.Version.MajorRevision, Environment.Version.Build);
			var exWpfDir = string.Format(@"C:\WINDOWS\Microsoft.NET\Framework\v{0}\WPF", ver);
			var exDir = string.Format(@"C:\WINDOWS\Microsoft.NET\Framework\v{0}", ver);

			new[]
				{
					typeof(Action<>).Assembly.Location,
					//typeof(object).Assembly.Location, // mscorelib
					typeof(Enumerable).Assembly.Location, // System.Core
					//typeof(FrameworkElement).Assembly.Location, // PresentationFramework
					typeof(UIElement).Assembly.Location, // PresentationCore
					typeof(DependencyObject).Assembly.Location, // WindowsBase
					//typeof(XamlReader).Assembly.Location, // System.Xaml
				    typeof(Debug).Assembly.Location, // System
				    typeof(Microsoft.CSharp.RuntimeBinder.Binder).Assembly.Location, // Microsoft.CSharp
				    GetType().Assembly.Location
				}
				.ForEach(it => cp.ReferencedAssemblies.Add(it));

			cp.GenerateExecutable = false;
			cp.GenerateInMemory = true;
			cp.IncludeDebugInformation = false;
			cp.TreatWarningsAsErrors = false;
			cp.CompilerOptions = string.Format("/lib:{0}", exWpfDir);
			cp.CompilerOptions = string.Format("/lib:{0}", exDir);

			var result = provider.CompileAssemblyFromSource(cp, GetCompilationSourceCode());
			if (result.Errors.HasErrors)
			{
				var errorMessage = string.Empty;
				foreach (CompilerError error in result.Errors)
					errorMessage += "\r\n\t" + error.ErrorText;

				throw new Exception(errorMessage);
			}

			var type = result.CompiledAssembly.GetType("_Dynamic");
			var instance = Activator.CreateInstance(type, _hostObject);
			return type.GetMethod("Invoke").Invoke(instance, new object[] { });
		}

		private string GetCompilationSourceCode()
		{
			const string sc = @"
using System;
using System.Diagnostics;
using System.Windows;
using System.Collections.Generic;
using System.Linq;
using Byond;
using Byond.HostObjects;

public class _Dynamic
{
	public _Dynamic(dynamic hostObject)
	{
		Host = hostObject;
	}

	public dynamic Host { get; private set; }

	public object Invoke()
	{
		__code__
	}
}";
			return sc.Replace("__code__", SourceCode);
		}
	}
}